package net.sk.china.common.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import net.sk.china.common.constants.Constants;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.vo.CreateOrderVo;
import net.sk.china.common.vo.WxRefundVo;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * +----------------------------------------------------------------------
 * | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2018-2024 聊来All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * +----------------------------------------------------------------------
 * | @Author: 聊来 <18970881148@qq.com>
 * +----------------------------------------------------------------------
 * | DateTime: 2024/6/2 22:36
 * +----------------------------------------------------------------------
 */
public class WxPayUtil {
    /**
     * 处理 HTTPS API返回数据，转换成Map对象。return_code为SUCCESS时，验证签名。
     *
     * @param xmlStr API返回的XML格式数据
     * @return {@code HashMap<String, Object>}
     */
    public static HashMap<String, Object> processResponseXml(String xmlStr) throws Exception {
        String returnCode = "return_code";
        String code;
        HashMap<String, Object> respData = XmlUtil.xmlToMap(xmlStr);
        if (respData.containsKey(returnCode)) {
            code = (String) respData.get(returnCode);
        } else {
            throw new TalkException(String.format("No `return_code` in XML: %s", xmlStr));
        }

        if (code.equals(Constants.FAIL)) {
            return respData;
        } else if (code.equals(Constants.SUCCESS)) {
            return respData;
        } else {
            throw new TalkException(String.format("return_code value %s is invalid in XML: %s", code, xmlStr));
        }
    }


    /**
     * 获取随机字符串，长度要求在32位以内。
     */
    public static String getNonceStr() {
        return SecureUtil.md5(TalkUtil.getUuid() + TalkUtil.randomCount(111111, 666666));
    }

    /**
     * 获取sign
     * @param vo      微信公共下单对象
     * @param signKey 微信签名key
     * @return {@code String}
     */
    public static String getSign(CreateOrderVo vo, String signKey) {
        // 对象转map
        Map<String, Object> map = JSONObject.parseObject(JSONObject.toJSONString(vo), Map.class);
        // map排序
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(Constants.FIELD_SIGN)) {
                continue;
            }
            // 参数值为空，则不参与签名
            if (ObjectUtil.isNotNull(map.get(k))) {
                sb.append(k).append("=").append(map.get(k)).append("&");
            }
        }
        sb.append("key=").append(signKey);
        String sign = SecureUtil.md5(sb.toString()).toUpperCase();
        return sign;
    }

    /**
     * 获取sign
     * @param wxRefundVo  微信退款对象
     * @param signKey 微信签名key
     * @return {@code String}
     */
    public static String getSign(WxRefundVo wxRefundVo, String signKey) {
        // 对象转map
        Map<String, Object> map = JSONObject.parseObject(JSONObject.toJSONString(wxRefundVo), Map.class);
        // map排序
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(Constants.FIELD_SIGN)) {
                continue;
            }
            // 参数值为空，则不参与签名
            if (ObjectUtil.isNotNull(map.get(k))) {
                sb.append(k).append("=").append(map.get(k)).append("&");
            }
        }
        sb.append("key=").append(signKey);
        String sign = SecureUtil.md5(sb.toString()).toUpperCase();
        return sign;
    }

    /**
     * 获取sign
     * @param map      待签名数据
     * @param signKey 微信签名key
     * @return {@code String}
     */
    public static String getSign(Map<String, String> map, String signKey) {
        // map排序
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(Constants.FIELD_SIGN)) {
                continue;
            }
            // 参数值为空，则不参与签名
            if (StrUtil.isNotBlank(map.get(k)) && map.get(k).trim().length() > 0) {
                sb.append(k).append("=").append(map.get(k).trim()).append("&");
            }
        }
        sb.append("key=").append(signKey);
        return SecureUtil.md5(sb.toString()).toUpperCase();
    }

    /**
     * 获取sign
     * @param map      待签名数据
     * @param signKey 微信签名key
     * @return {@code String}
     */
    public static String getSignObject(Map<String, Object> map, String signKey) {
        // map排序
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(Constants.FIELD_SIGN)) {
                continue;
            }
            // 参数值为空，则不参与签名
            if (ObjectUtil.isNotNull(map.get(k))) {
                sb.append(k).append("=").append(map.get(k)).append("&");
            }
        }
        sb.append("key=").append(signKey);
        String sign = SecureUtil.md5(sb.toString()).toUpperCase();
        return sign;
    }

    /**
     * 获取当前时间戳，单位秒
     * @return  {@code Long}
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

    /**
     * 获取当前时间戳，单位毫秒
     * @return  {@code Long}
     */
    public static Long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

    /**
     * XML格式字符串转换为Map
     * @param strXML XML字符串
     * @return {@code Map<String, String>}
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<>(32);
            DocumentBuilder documentBuilder = WxPayXmlUtil.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes(StandardCharsets.UTF_8));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            System.out.println(StrUtil.format("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML));
            throw ex;
        }

    }
}
